<?php
/**
 * BlockChain.php
 * Dinosaur
 * Created on 2021/12/17 16:26
 * Create by lijunwei
 */

namespace app\api\service;

use app\admin\model\DinosaurDetail;
use app\admin\model\MemberEarningDetail;
use app\admin\model\DinosaurList;
use app\admin\model\FightDetail;
use app\admin\model\Member;
use app\admin\model\MyDinosaur;
use app\admin\model\OpenEggDetail;
use app\admin\model\SourceBlockDetail;
use app\admin\model\TransactionDetail;
use think\Db;
use think\facade\Cache;
use think\Model;

class BlockChain
{
//    public function dinosaurBazaar()
//    {
//        $model = new DinosaurList();
//        $ethereum_url = config('app.ethereum_url');
//        $url = $ethereum_url . 'api/totalOrderNumber';
//        $header = array(
//            'Accept: application/json',
//            'apiKey: ' . config('app.apiKey'),
//        );
//        $info = httpUrl($url, false, $header);
//        $num = json_decode($info, true);
//        if ($num > 0) {
////        $count = $model->getCount();
////        if ($num > $count) {
//            $list_url = $ethereum_url . "api/getOrderLists";
////            $params = ['lower' => $count, 'upper' => $num - 1];
//            $params = ['lower' => 0, 'upper' => $num - 1];
//            $lists = httpUrl($list_url, $params, $header);
//            $lists = json_decode($lists, true);
//            $data = [];
//            foreach ($lists as $key => $list) {
//                $on_sale = 0;
//                if ($list[6]) {
//                    $on_sale = 1;
//                }
//                $data[$key] = [
//                    'order_number' => $list[0],
//                    'contract_address' => $list[1],
//                    'owner' => $list[2],
//                    'token_id' => $list[3],
//                    'residue_num' => $list[4],
//                    'price' => $list[5],
//                    'on_sale' => $on_sale,
//                    'add_time' => time(),
//                    'updated_time' => time(),
//                ];
//            }
//            // 先删除所有数据，再添加新的数据
//            Db::name('dinosaur_list')->delete(true);
//            $model->replace()->insertAll($data);
//            return true;
////        }
////        return false;
//        }
//    }

    /**
     * @Title: info
     * @Describe 合约基础信息
     * @return mixed
     * @author lijunwei
     * @Date 2022/1/4 16:33
     */
    public function info()
    {
        $ethereum_url = config('app.ethereum_url');
        $url = $ethereum_url . 'api/info';
        $header = array(
            'Accept: application/json',
            'apiKey: ' . config('app.apiKey'),
        );
        $info = httpUrl($url, false, $header);
        return json_decode($info, true);
    }

    /**
     * @Title: userNumber
     * @Describe 获取用户总数
     * @return mixed
     * @author lijunwei
     * @Date 2021/12/19 10:16
     */
    public function userNumber()
    {
        $lists = $this->getUsers();
        $data = [];
        foreach ($lists as $key => $list) {
            $addTime = strtotime($list['createdAt']) - 8 * 3600;
            $updateTime = strtotime($list['updatedAt']) - 8 * 3600;
            $data[$key] = [
                'user_address' => "'" . $list['userAddress'] . "'",
                'add_time' => $addTime,
                'updated_time' => $updateTime,
            ];
        }

        // 存在则更新，不存在则添加
        $sql = "INSERT INTO dn_member (user_address,add_time,updated_time) VALUES ";
        $tmp = array();
        foreach ($data as $k => $v) {
            $tmp[] = '(' . implode(',', ($v)) . ')';
        }
//        $sum = count($tmp);
//        unset($tmp[$sum - 1]);
        $sql .= implode(',', $tmp);
        $sql .= " ON DUPLICATE KEY UPDATE `user_address`= VALUES(user_address)";
        Db::execute($sql);
        return true;
    }

    /**
     * @Title: userList
     * @Describe 用户恐龙数、金币数、探险数获取
     * @return mixed
     * @author lijunwei
     * @Date 2021/12/19 11:22
     */
    public function userList()
    {
        $model = new Member();
        $lists = $this->getUsers();
        foreach ($lists as $list) {
            $pid = "";
            $father = $this->getUserFather($list['userAddress']);
            if ($father != '0x0000000000000000000000000000000000000000') {
                $pid = $father;
            }
            $data = [
                'dinosaur_num' => $this->getUserDinosaurNum($list['userAddress']),
                'gold_coin_num' => $this->getUserCoinNum($list['userAddress']),
//                'adventure_num' => $this->getUserAdventureNum($list['userAddress']),
                'pid' => $pid,
            ];
            if ($data['dinosaur_num'] == 0 && $data['gold_coin_num'] == 0 && $data['pid'] == "") {
                return true;
            }
            $where = ['user_address' => $list['userAddress']];
            $model->updateMember($where, $data);
        }
        return true;
    }

    /**
     * @Title: getUsers
     * @Describe 获取所有用户数
     * @return mixed
     * @author lijunwei
     * @Date 2021/12/19 11:25
     */
    public function getUsers()
    {
        $ethereum_url = config('app.ethereum_url');
        $url = $ethereum_url . 'api/users';
        $header = array(
            'Accept: application/json',
            'apiKey: ' . config('app.apiKey'),
        );
        $info = httpUrl($url, false, $header);
        return json_decode($info, true);
    }

    /**
     * @Title: getUserDinosaurNum
     * @Describe 获取用户恐龙数
     * @return mixed
     * @author lijunwei
     * @Date 2021/12/19 11:28
     */
    public function getUserDinosaurNum($userAddress)
    {
        $ethereum_url = config('app.ethereum_url');
        $url = $ethereum_url . 'api/getUserDinosaurAmount';
        $header = array(
            'Accept: application/json',
            'apiKey: ' . config('app.apiKey'),
        );
        $params = ['userAddress' => $userAddress];
        $info = httpUrl($url, $params, $header);
        return (int)$info;
    }

    /**
     * @Title: getUserCoinNum
     * @Describe 获取用户金币数
     * @param $userAddress
     * @return int
     * @author lijunwei
     * @Date 2021/12/19 11:37
     */
    public function getUserCoinNum($userAddress)
    {
        $ethereum_url = config('app.ethereum_url');
        $url = $ethereum_url . 'api/userCoinTotalReward';
        $header = array(
            'Accept: application/json',
            'apiKey: ' . config('app.apiKey'),
        );
        $params = ['userAddress' => $userAddress];
        $info = httpUrl($url, $params, $header);
        return $info;
    }

    /**
     * @Title: getUserAdventureNum
     * @Describe 获取用户的探险次数
     * @param $userAddress
     * @return int
     * @author lijunwei
     * @Date 2021/12/19 11:38
     */
    public function getUserAdventureNum($userAddress)
    {
        $ethereum_url = config('app.ethereum_url');
        $url = $ethereum_url . 'api/userAttackTime';
        $header = array(
            'Accept: application/json',
            'apiKey: ' . config('app.apiKey'),
        );
        $params = ['userAddress' => $userAddress];
        $info = httpUrl($url, $params, $header);
        return (int)$info;
    }

    /**
     * @Title: getUserFather
     * @Describe 获取用户的上级
     * @param $userAddress
     * @return int
     * @author lijunwei
     * @Date 2021/12/19 11:39
     */
    public function getUserFather($userAddress)
    {
        $ethereum_url = config('app.ethereum_url');
        $url = $ethereum_url . 'api/userFather';
        $header = array(
            'Accept: application/json',
            'apiKey: ' . config('app.apiKey'),
        );
        $params = ['userAddress' => $userAddress];
        return httpUrl($url, $params, $header);
    }

    /**
     * 战斗事件
     * @param $attack_lists
     * @throws \think\Exception
     */
    public function getAttack($attack_lists)
    {
//        1. 枚举类型1 自己组装后替换为时间戳了
//        2. 打怪的玩家地址
//        3. 打怪的boss号码 （0，1，2）
//        4. 恐龙级别 0-7 枚举类型
//        5. 恐龙的NFTID
//        6. 恐龙剩余的次数
//        7. 恐龙的冷却时间 （返回十进制时间戳）
//        8. 备用
        $dinosaur_level_id = hexdec($attack_lists[3]);
        $dinosaur = $this->dinosaurInfo($dinosaur_level_id);
        $userAddress = '0x' . substr($attack_lists[1], 24, 64);
        $master_name = config('app.monster')[hexdec($attack_lists[2])];
        $fight = [
            'nft_id' => hexdec($attack_lists[4]),
            'user_address' => $userAddress,
            'monster_name' => $master_name,
            'monster_level_id' => hexdec($attack_lists[2]),
            'dinosaur_level_id' => $dinosaur_level_id,
            'dinosaur_name' => $dinosaur['name'],
            'residue_num' => hexdec($attack_lists[5]),
            'fight_gold' => config('attackCost')[$dinosaur_level_id],
            'add_time' => date("Y-m-d H:i:s", hexdec($attack_lists[0])),
        ];
        FightDetail::getInstance()->insertGetId($fight);
        // 更新恐龙的战斗状态：Attack事件存在的恐龙都是战斗中的恐龙
        $where = ['nft_id' => hexdec($attack_lists[4]), 'owner_address' => $userAddress, 'dinosaur_level_id' => $dinosaur_level_id];
        $result = ['fight_status' => 2];
        DinosaurDetail::getInstance()->updateDinosaur($where, $result);
        // 用户探险次数自增1
        Member::getInstance()->where(['user_address' => $userAddress])->setInc('adventure_num');
    }

    /**
     * @Title: getEgg
     * @Describe 获取开蛋的奖励,恐龙也在这里铸造
     * @param $lists
     * @param $params
     * @author lijunwei
     * @Date 2021/12/31 10:49
     */
    public function getEgg($list)
    {
        // topics里面下标为1代表恐龙等级，下标为2代表恐龙nft id，下标为3代表金蛋结果
        // 类型的话，就是金蛋1，银蛋0两个类型
        // 蛋的价格是固定的，金蛋400，银蛋200
        // 任何人的第1个蛋，都是赠送蛋
        // getEgg 获取开蛋奖励 类型5
        // 查询目标 GameMaster
        // 1. 枚举类型5
        // 2. 玩家地址
        // 3. 奖励金币数
        // 4. 得到的恐龙级别，没得到恐龙就是99
        // 5. 得到的恐龙NFTID，没得到就是0
        // 6. 是否赠送蛋,0 就是赠送蛋
        // 7. 提交的时间戳
        $is_give = 1;
        if (hexdec($list[5]) == 0) {
            $is_give = 2;
        }
        $userAddress = '0x' . substr($list[1], 24, 64);
        if (hexdec($list[3]) == 99) {  // 开的是银蛋
            $eggs = [
                'nft_id' => hexdec($list[4]),
                'user_address' => $userAddress,
                'type' => 0,
                'is_give' => $is_give,
                'trans_price' => getConfig('SILVER_COIN'),
                'egg_result' => inGold($list[2]),
                'add_time' => date("Y-m-d H:i:s", hexdec($list[0])),
            ];
        } else { // 开的是恐龙(金蛋)
            $eggs = [
                'nft_id' => hexdec($list[4]),
                'user_address' => $userAddress,
                'type' => 1,
                'is_give' => $is_give,
                'trans_price' => getConfig('GOLD_COIN'),
                'egg_result' => inGold($list[2]),
                'add_time' => date("Y-m-d H:i:s", hexdec($list[0])),
            ];
            $dinosaur = $this->dinosaurInfo(hexdec($list[3]));
            $result = [
                'nft_id' => hexdec($list[4]),
                'owner_address' => $userAddress,
                'dinosaur_level_id' => hexdec($list[3]),
                'dinosaur_name' => $dinosaur['name'],
                'fight_status' => 1,
                'trans_status' => 1,
                'add_time' => hexdec($list[0]),
            ];
        }
        OpenEggDetail::getInstance()->insert($eggs);
        if (!empty($result)) {
            DinosaurDetail::getInstance()->insert($result);
            // 如果是金蛋，则添加上级和上上级用户的收益
            // 判断当前地址是否有上级和上上级  如果只有上级，则是一维数组，否则是二维数组
            $father = $this->getUserFather($userAddress);
            $pid = "";
            if ($father != '0x0000000000000000000000000000000000000000') {
                $pid = $father;
            }
            if (!empty($pid)) {  // 存在上级，则往下走
                $tFather = $this->getUserFather($pid);
                $tPid = "";
                if ($tFather != '0x0000000000000000000000000000000000000000') {
                    $tPid = $tFather;
                }
                if (!empty($tPid)) { // 说明有两级
                    $earnings = [
                        [
                            'pid' => $pid,
                            'user_address' => $userAddress,
                            'trans_price' => getConfig('GOLD_COIN'),
                            'earning' => getConfig('GOLD_COIN') * 0.1,
                            'add_time' => hexdec($list[0]),
                        ],
                        [
                            'pid' => $tPid,
                            'user_address' => $userAddress,
                            'trans_price' => getConfig('GOLD_COIN'),
                            'earning' => getConfig('GOLD_COIN') * 0.05,
                            'add_time' => hexdec($list[0]),
                        ],
                    ];
                } else {  // 说明只有一级
                    $earnings = [
                        'pid' => $pid,
                        'user_address' => $userAddress,
                        'trans_price' => getConfig('GOLD_COIN'),
                        'earning' => getConfig('GOLD_COIN') * 0.1,
                        'add_time' => hexdec($list[0]),
                    ];
                }
                MemberEarningDetail::getInstance()->insertAll($earnings);
            }
        }
    }

    /**
     * @Title: dinosaurInfo
     * @Describe 根据级别获取恐龙详情
     * @param $level
     * @return mixed
     * @author lijunwei
     * @Date 2021/12/30 16:51
     */
    public function dinosaurInfo($level)
    {
        $path = ROOT_PATH . "public/nft/{$level}/metadata/json/0.json";
        $string = file_get_contents($path);
        return json_decode($string, true);
    }


    /**
     * @Title: getContractEvent
     * @Describe 根据合约获取数据
     * @return int
     * @throws \think\Exception
     * @author lijunwei
     * @Date 2022/1/4 16:32
     */
    public function getContractEvent()
    {
        // 如果在15秒内还没有执行完，则直接返回
        if (Cache::has('getContractEvent') && Cache::get('getContractEvent') == 'start') {
            return 1;
        }
        Cache::set('getContractEvent', "start");
        $contracts = $this->info();

        $ethereum_url = config('app.ethereum_url');
        // 返回最新的区块号
        $new_block_url = $ethereum_url . 'api/getBlockNumber';
        $header = array(
            'Accept: application/json',
            'apiKey: ' . config('app.apiKey'),
        );
        $block_number = httpUrl($new_block_url, false, $header);

        $block_url = $ethereum_url . 'api/getContractEvent';
        if (Cache::has('block_number')) {
            $startBlock = Cache::get('block_number');
            // 如果缓存中的区块号和最新获取的区块号一样，说明没有新的数据，直接返回就好
            if ($startBlock == $block_number) {
                return 1;
            }
        } else {
            $startBlock = $contracts['StartBlockNumber'];
        }
        Cache::set('block_number', $block_number);
        // NFTSwap合约
        $params = [   //
            'fromBlock' => $startBlock,
            'toBlock' => $block_number,
            'event' => config('app.event'),
            'contract' => $contracts['NFTSwapAddress']
        ];
        // var_dump($params);
        $swap_number = httpUrl($block_url, $params, $header);
        $lists = json_decode($swap_number, true);
        if (!empty($lists)) {
            foreach ($lists as $list) {
                $data = substr($list['data'], 2);
                $data = str_split($data, 64);
                $type = hexdec(substr($list['topics'][1], -2));
                array_unshift($data, $list['timeStamp']);
                switch ($type) {
                    case 13 :
                        echo $type . "buy \n";
                        array_push($data, $list['transactionHash']);
                        $this->buy($data);
                        break;
                    case 14 :
                        echo $type . "Sell \n";
                        $this->sell($data);
                        break;
                    case 15 :
                        echo $type . "unSell \n";
                        $this->unSell($data);
                        break;
                    default:
                        echo $type . "\n";
                }
            }
        }
        // GameMaster合约
        $attack_params = [
            'fromBlock' => $startBlock,
            'toBlock' => $block_number,
            'event' => config('app.event'),
            'contract' => $contracts['GameMasterAddress']
        ];
        $gameMaster = httpUrl($block_url, $attack_params, $header);
        $gameMasterLists = json_decode($gameMaster, true);
        if (!empty($gameMasterLists)) {
            foreach ($gameMasterLists as $list) {
                $data = substr($list['data'], 2);
                $data = str_split($data, 64);
                $type = hexdec(substr($list['topics'][1], -2));
                array_unshift($data, $list['timeStamp']);
                switch ($type) {
                    case 1 :
                        echo $type . "getAttack \n";
                        $this->getAttack($data);
                        break;
                    case 2 :
                        echo $type . "getAttackResult \n";
                        $this->getAttackResult($data);
                        break;
                    case 5 :
                        echo $type . "getEgg \n";
                        $this->getEgg($data);
                        break;
                    case 6 :
                        echo $type . "setInviter \n";;
                        $this->setInviter($data);
                        break;
                    default:
                        echo $type . "\n";
                }
            }
        }
        // 原始数据
        $source = [
            'swap_param' => json_encode($params),
            'swap_result' => json_encode($lists),
            'game_param' => json_encode($params),
            'game_result' => json_encode($lists),
            'add_time' => time(),
        ];
        SourceBlockDetail::getInstance()->insertGetId($source);

        Cache::set('getContractEvent', "end");
        return 1;
    }

    /**
     * @Title: getAttackResult
     * @Describe  更新attack返回结果的数据
     * @param $data
     * @author lijunwei
     * @Date 2022/1/4 10:59
     */
    public function getAttackResult($data)
    {
//        getAttackResult 类型2
//        查询目标 GameMaster
//        日志
//        1. 枚举类型2
//        2. 打怪的玩家地址
//        3. 随机数结果
//        4. 本获得奖励数量
//        5. 玩家总奖励数量
//        6. 恐龙的级别
//        7. 恐龙的NFTID

        // 更新战斗明细中恐龙的drop_gold（掉落INGold）
        $userAddress = '0x' . substr($data[1], 24, 64);
        $new_gold = inGold($data[3]);
        $total_gold = inGold($data[4]);
        $where = ['nft_id' => hexdec($data[6]), 'user_address' => $userAddress, 'dinosaur_level_id' => hexdec($data[5])];
        $param = ['drop_gold' => $new_gold];
        FightDetail::getInstance()->editByWhere($where, $param);

        // 更新用户的gold_coin_num金币数据
        $gold_coin_num = hexdec($data[3]);
        $sql = "update dn_member set gold_coin_num=gold_coin_num+{$gold_coin_num} where user_address = '" . $userAddress . "'";
        Db::execute($sql);
       // $member_where = ['user_address' => $userAddress];
       // $member_param = ['in_gold_num' => $total_gold];
       // Member::getInstance()->updateMember($member_where, $member_param);

        // 更新恐龙的战斗状态：getAttackResult事件存在的恐龙都是未战斗的恐龙
        $where = ['nft_id' => hexdec($data[6]), 'owner_address' => $userAddress, 'dinosaur_level_id' => hexdec($data[5])];
        $result = ['fight_status' => 1];
        DinosaurDetail::getInstance()->updateDinosaur($where, $result);
    }

    /**
     * @Title: setInviter
     * @Describe 更新玩家的上级地址
     * @param $data
     * @author lijunwei
     * @Date 2022/1/4 18:11
     */
    public function setInviter($data)
    {
        $userAddress = '0x' . substr($data[1], 24, 64);
        $pid = substr($data[2], 24, 64);
        // 如果上级地址和自己的地址不一致，则添加到用户表里面去
        if ($userAddress != $pid) {
            $member_where = ['user_address' => $userAddress];
            $member_param = ['pid' => $pid];
            Member::getInstance()->updateMember($member_where, $member_param);
        }
    }

    /**
     * @Title: buy
     * @Describe 买恐龙(就是交易明细)
     * @param $data
     * @author lijunwei
     * @Date 2022/1/4 18:54
     */
    public function buy($data)
    {
//        Buy恐龙 13
//        查询目标 NFTSwap
//        1. 枚举类型13
//        2. 卖家地址
//        3. 买家地址
//        4. 购买价格
//        5. 订单号
//        6. 恐龙级别
//        7. 恐龙NFTID
        $dinosaur = $this->dinosaurInfo(hexdec($data[5]));
        $result = [
            'transaction_no' => hexdec($data[4]),
            'nft_id' => hexdec($data[6]),
            'seller_address' => '0x' . substr($data[1], 24, 64),
            'buyers_address' => '0x' . substr($data[2], 24, 64),
            'dinosaur_name' => $dinosaur['name'],
            'dinosaur_level_id' => hexdec($data[5]),
            'trans_status' => 1,
            'trans_price' => inGold($data[3]),
            'trans_txid' => $data[7],
            'return_time' => hexdec($data[0]),
        ];
        TransactionDetail::getInstance()->insertGetId($result);
    }

    /**
     * @Title: sell
     * @Describe 获取并更新恐龙的状态
     * @param $data
     * @author lijunwei
     * @Date 2022/1/5 15:33
     */
    public function sell($data)
    {
        //        Sell恐龙 14
        //        查询目标 NFTSwap
        //        1. 枚举类型14
        //        2. 卖家地址
        //        3. 订单号
        //        4. 售卖价格
        //        5. 恐龙级别
        //        6. 恐龙NFTID
        //        7. 备用
        $userAddress = '0x' . substr($data[1], 24, 64);
        // 更新恐龙的交易状态：sell事件存在的恐龙都是交易中的恐龙
        $where = ['nft_id' => hexdec($data[5]), 'owner_address' => $userAddress, 'dinosaur_level_id' => hexdec
        ($data[4])];
        $result = ['trans_status' => 2];
        DinosaurDetail::getInstance()->updateDinosaur($where, $result);
    }

    /**
     * @Title: unSell
     * @Describe 下架的恐龙
     * @param $data
     * @author lijunwei
     * @Date 2022/1/5 15:37
     */
    public function unSell($data)
    {
        //        UnSell恐龙 15
        //        查询目标 NFTSwap
        //        1. 枚举类型15
        //        2. 玩家地址
        //        3. 订单号
        //        4. 恐龙级别
        //        5. 恐龙NFTID
        //        6. 备用
        //        7. 备用
        $userAddress = '0x' . substr($data[1], 24, 64);
        // 更新恐龙的交易状态：sell事件存在的恐龙都是待出售(下架)的恐龙
        $where = ['nft_id' => hexdec($data[4]), 'owner_address' => $userAddress, 'dinosaur_level_id' => hexdec
        ($data[3])];
        $result = ['trans_status' => 1];
        DinosaurDetail::getInstance()->updateDinosaur($where, $result);
    }

    /**
     * @Title: goldRate
     * @Describe 战斗明细表的金币效率更新
     * @author lijunwei
     * @Date 2022/1/5 13:39
     */
    public function goldRate()
    {
        $where = ['gold_efficiency' => ''];
        $fightList = FightDetail::getInstance()->getListByPage($where, 1, 50);
        foreach ($fightList as $list) {
            if (empty($list['drop_gold'])) {
                $rate = 0;
            } else {
                $rate = $list['fight_gold'] / $list['drop_gold'];
            }
            $where = ['id' => $list['id']];
            $data = ['gold_efficiency' => keep($rate, 4)];
            FightDetail::getInstance()->editByWhere($where, $data);
        }

        return true;
    }

    /**
     * @Title: getDinOrINGold
     * @Describe 获取用户的din和INGold(后台用户地址管理使用)
     * @author lijunwei
     * @Date 2022/1/5 15:51
     */
    public function getDinOrINGold($address)
    {
        $ethereum_url = config('app.ethereum_url');
        // 返回最新的区块号
        $new_block_url = $ethereum_url . 'api/batchBalanceOf';
        $params = [
            'userAddress' => $address
        ];
        $header = array(
            'Accept: application/json',
            'apiKey: ' . config('app.apiKey'),
        );
        $block_number = httpUrl($new_block_url, $params, $header);
        return json_decode($block_number, true);
    }
}